from fastapi import APIRouter, Depends, HTTPException, status, Header
from fastapi.security import OAuth2PasswordRequestForm, OAuth2PasswordBearer
from src.backend.utils.prismaUtils import getPrismaClient
from src.backend.utils.jwtUtils import getHashedPassword, verifyPassword, createAccessToken, createRefreshToken, isBlacklisted, addToBlacklist
from src.backend.schemas.userSchemas import UserCreate, UserOut, TokenSchema, TokenPayload, RegisterResponse, UserLogin, LoginResponse, RefreshTokenRequest, LogoutRequest
from prisma.models import User
from jose import jwt as JoseJwt, JWTError
from src.backend.utils.envLoader import appConfig
from datetime import datetime
from typing import Any, Optional

router = APIRouter(prefix="/auth", tags=["Auth"])

def userToUserOut(user):
    return UserOut(
        id=user.id,
        username=user.username,
        permission=user.permission,
        createdAt=user.createdAt.isoformat() if hasattr(user.createdAt, 'isoformat') else str(user.createdAt),
        updatedAt=user.updatedAt.isoformat() if hasattr(user.updatedAt, 'isoformat') else str(user.updatedAt),
    )

@router.post("/register", summary="注册新用户", response_model=RegisterResponse)
async def register(userCreate: UserCreate):
    # UserCreate 模型已经包含了字段验证逻辑
    prisma = await getPrismaClient()
    
    try:
        # 检查用户名是否已存在
        exist = await prisma.user.find_unique(where={"username": userCreate.username})
        if exist:
            raise HTTPException(status_code=400, detail="用户名已存在")
        
        hashedPassword = await getHashedPassword(userCreate.password)
        # 创建新用户
        user = await prisma.user.create(data={
            "username": userCreate.username,
            "password": hashedPassword,
            "permission": "user"  # 默认权限为普通用户
        })
        
        # 自动生成令牌
        accessToken = createAccessToken(user.username)
        refreshToken = createRefreshToken(user.username)
        
        # 返回用户信息和令牌
        return RegisterResponse(
            user=userToUserOut(user),
            accessToken=accessToken,
            refreshToken=refreshToken
        )
    except Exception as e:
        # 捕获其他可能的异常
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(status_code=500, detail=f"注册失败: {str(e)}")

@router.post("/login", summary="用户登录", response_model=LoginResponse)
async def login(userLogin: UserLogin):
    try:
        prisma = await getPrismaClient()
        user = await prisma.user.find_unique(where={"username": userLogin.username})
        
        # 验证用户存在和密码正确
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名不存在",
                headers={"WWW-Authenticate": "Bearer"},
            )
            
        isPasswordCorrect = await verifyPassword(userLogin.password, user.password)
        if not isPasswordCorrect:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="密码错误",
                headers={"WWW-Authenticate": "Bearer"},
            )
        
        # 生成访问令牌和刷新令牌
        accessToken = createAccessToken(user.username)
        refreshToken = createRefreshToken(user.username)
        
        # 返回用户信息和令牌
        return LoginResponse(
            user=userToUserOut(user),
            accessToken=accessToken,
            refreshToken=refreshToken
        )
    except Exception as e:
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"登录失败: {str(e)}",
        )

@router.post("/login/oauth", summary="OAuth2 用户登录", response_model=LoginResponse)
async def oauthLogin(formData: OAuth2PasswordRequestForm = Depends()):
    """
    OAuth2 兼容的登录端点，用于支持标准 OAuth2 客户端
    """
    try:
        prisma = await getPrismaClient()
        user = await prisma.user.find_unique(where={"username": formData.username})
        
        # 验证用户存在和密码正确
        if not user:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名不存在",
                headers={"WWW-Authenticate": "Bearer"},
            )
            
        isPasswordCorrect = await verifyPassword(formData.password, user.password)
        if not isPasswordCorrect:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="用户名或密码错误",
                headers={"WWW-Authenticate": "Bearer"},
            )
        
        # 生成访问令牌和刷新令牌
        accessToken = createAccessToken(user.username)
        refreshToken = createRefreshToken(user.username)
        
        # 返回用户信息和令牌
        return LoginResponse(
            user=userToUserOut(user),
            accessToken=accessToken,
            refreshToken=refreshToken
        )
    except Exception as e:
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"登录失败: {str(e)}",
        )

@router.post("/refresh", summary="刷新访问令牌", response_model=TokenSchema)
async def refreshAccessToken(refreshRequest: RefreshTokenRequest):
    """
    使用刷新令牌获取新的访问令牌
    """
    try:
        # 验证刷新令牌
        payload = JoseJwt.decode(
            refreshRequest.refreshToken, 
            appConfig.JWT_REFRESH_SECRET_KEY, 
            algorithms=[appConfig.JWT_ALGORITHM]
        )
        tokenData = TokenPayload(**payload)
        
        # 检查令牌是否过期
        if datetime.fromtimestamp(tokenData.exp) < datetime.now():
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED, 
                detail="刷新令牌已过期，请重新登录"
            )
            
        # 验证用户是否存在
        prisma = await getPrismaClient()
        user = await prisma.user.find_unique(where={"username": tokenData.sub})
        if not user:
            raise HTTPException(status_code=404, detail="用户不存在")
            
        # 生成新的访问令牌和刷新令牌
        accessToken = createAccessToken(user.username)
        refreshToken = createRefreshToken(user.username)
        
        return TokenSchema(
            accessToken=accessToken,
            refreshToken=refreshToken
        )
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效的刷新令牌",
            headers={"WWW-Authenticate": "Bearer"},
        )
    except Exception as e:
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"刷新令牌失败: {str(e)}",
        )

oauth2Scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login/oauth", scheme_name="JWT")

async def getCurrentUser(token: str = Depends(oauth2Scheme)) -> UserOut:
    try:
        # 检查令牌是否在黑名单中
        if isBlacklisted(token):
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED, 
                detail="令牌已被注销，请重新登录"
            )
            
        # 验证令牌
        payload = JoseJwt.decode(token, appConfig.JWT_SECRET_KEY, algorithms=[appConfig.JWT_ALGORITHM])
        tokenData = TokenPayload(**payload)
        
        # 检查令牌是否过期
        if datetime.fromtimestamp(tokenData.exp) < datetime.now():
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED, 
                detail="令牌已过期，请重新登录"
            )
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无效的认证凭证",
            headers={"WWW-Authenticate": "Bearer"},
        )
    except Exception as e:
        if isinstance(e, HTTPException):
            raise e
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, 
            detail="认证失败"
        )
        
    # 获取用户信息
    prisma = await getPrismaClient()
    user = await prisma.user.find_unique(where={"username": tokenData.sub})
    if not user:
        raise HTTPException(status_code=404, detail="用户不存在")
        
    return userToUserOut(user)

@router.get("/me", summary="获取当前用户信息", response_model=UserOut)
async def getMe(currentUser: UserOut = Depends(getCurrentUser)):
    return currentUser

@router.post("/logout", summary="用户注销", status_code=status.HTTP_200_OK)
async def logout(
    logoutRequest: Optional[LogoutRequest] = None,
    authorization: Optional[str] = Header(None)
):
    """
    注销当前用户，使令牌失效
    
    可以通过以下方式提供令牌:
    1. Authorization 请求头: Bearer {token}
    2. 请求体中的 token 字段
    """
    # 尝试从不同来源获取令牌
    actualToken = None
    
    # 1. 从请求体获取
    if logoutRequest and logoutRequest.token:
        actualToken = logoutRequest.token
    
    # 2. 从 Authorization 头获取
    if not actualToken and authorization:
        try:
            scheme, tokenValue = authorization.split()
            if scheme.lower() == "bearer":
                actualToken = tokenValue
        except ValueError:
            pass
    
    # 检查是否有令牌
    if not actualToken:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="未提供认证令牌",
            headers={"WWW-Authenticate": "Bearer"},
        )
    
    try:
        # 将令牌添加到黑名单
        addToBlacklist(actualToken)
        return {"message": "注销成功"}
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"注销失败: {str(e)}",
        )

@router.post("/logout/current", summary="注销当前用户", status_code=status.HTTP_200_OK)
async def logoutCurrent(token: str = Depends(oauth2Scheme)):
    """
    注销当前已认证用户
    """
    try:
        # 将当前令牌添加到黑名单
        addToBlacklist(token)
        return {"message": "注销成功"}
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"注销失败: {str(e)}",
        ) 